package com.dai.rpc.spring;

import com.dai.rpc.annotation.EnableRpcClient;
import com.dai.rpc.annotation.MyReference;
import com.dai.rpc.annotation.MyService;
import com.dai.rpc.config.MyRpcConfig;
import com.dai.rpc.factory.SingletonFactory;
import com.dai.rpc.netty.NettyServer;
import com.dai.rpc.netty.client.NettyClient;
import com.dai.rpc.proxy.MyRpcClientProxy;
import com.dai.rpc.register.zookeeper.ZKServiceDiscovery;
import com.dai.rpc.server.MyServiceProvider;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

@Component
public class MyRpcSpringBeanPostProcessor  implements BeanPostProcessor, BeanFactoryPostProcessor {


    private MyServiceProvider myServiceProvider;
    private MyRpcConfig myRpcConfig;
    private NettyClient nettyClient;
    private ZKServiceDiscovery zkServiceDiscovery;
    private NettyServer nettyServer;

    public MyRpcSpringBeanPostProcessor() {
        this.myServiceProvider = SingletonFactory.getInstance(MyServiceProvider.class);
        this.zkServiceDiscovery = SingletonFactory.getInstance(ZKServiceDiscovery.class);
        this.nettyServer = SingletonFactory.getInstance(NettyServer.class);
        nettyClient = SingletonFactory.getInstance(NettyClient.class);
    }


    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        if (beanFactory instanceof BeanDefinitionRegistry) {
            try {
                // 添加扫描器过滤器，获取EnableRpcClient实例

                // init scanner
                Class<?> scannerClass = ClassUtils.forName ( "org.springframework.context.annotation.ClassPathBeanDefinitionScanner",
                        MyRpcSpringBeanPostProcessor.class.getClassLoader () );
                Object scanner = scannerClass.getConstructor ( new Class<?>[]{BeanDefinitionRegistry.class, boolean.class} )
                        .newInstance ( new Object[]{(BeanDefinitionRegistry) beanFactory, true} );
                // add filter
                Class<?> filterClass = ClassUtils.forName ( "org.springframework.core.type.filter.AnnotationTypeFilter",
                        MyRpcSpringBeanPostProcessor.class.getClassLoader () );
                Object filter = filterClass.getConstructor ( Class.class ).newInstance (EnableRpcClient.class );
                Method addIncludeFilter = scannerClass.getMethod ( "addIncludeFilter",
                        ClassUtils.forName ( "org.springframework.core.type.filter.TypeFilter", MyRpcSpringBeanPostProcessor.class.getClassLoader () ) );
                addIncludeFilter.invoke ( scanner, filter );
                // scan packages
                Method scan = scannerClass.getMethod ( "scan", new Class<?>[]{String[].class} );
                scan.invoke ( scanner, new Object[]{"com.mszlu.rpc.annontation"} );
            } catch (Throwable e) {
                // spring 2.0
            }
        }
    }

    // 在Spring的bean调用初始化方法前后执行
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

        // bean已经实例化完成，但是在调用初始化方法之前
        EnableRpcClient annotation = bean.getClass().getAnnotation(EnableRpcClient.class);
        if(annotation != null){
            if(myRpcConfig == null){
                // 填充config属性
                myRpcConfig = new MyRpcConfig();
                myRpcConfig.setProviderPort(annotation.serverPort());
                myRpcConfig.setZKGroup(annotation.zookeeperGroup());
                myRpcConfig.setZKHost(annotation.zookeeperHost());
                myRpcConfig.setZKPort(annotation.zookeeperPort());
                myRpcConfig.setLoadBalancer(annotation.loadBalance());
                // 调用myServiceProvider的初始化方法
                myServiceProvider.init(myRpcConfig);
                zkServiceDiscovery.init(myRpcConfig);
                nettyServer.init(myRpcConfig);
                nettyClient.setMyRpcConfig(myRpcConfig);
            }



        }

        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        /**
         * 后置处理器主要做两件事情：
         * 1. 对加了MyService注解的类，把其服务发布
         * 2. 对字段上有MyReference注解的bean，为其创建代理类注入
         *
         * 注意：这两个注解不能单独使用，一定得配合Spring内部的注解，否则无法创建bean,走bean的生命周期进入该流程
         */
        // 判断bean上是否加了MyService注解
        if(bean.getClass().isAnnotationPresent(MyService.class)){
            // 发布服务
            MyService myService = bean.getClass().getAnnotation(MyService.class);
            myServiceProvider.publihService(myService, bean);
        }

        //  判断字段上是否有MyReference注解
        Class<?> aClass = bean.getClass();
        // 获取私有属性
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            MyReference myReference = field.getAnnotation(MyReference.class);
            if(myReference != null){
                // 为其创建代理类注入属性
                MyRpcClientProxy myRpcClientProxy = new MyRpcClientProxy(myReference,nettyClient);
                // 代理
                Object proxy = myRpcClientProxy.getProxy(field.getType());
                // 注入属性
                field.setAccessible(true);
                try {
                    field.set(bean,proxy);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }

        return bean;
    }
}
